package infra.jenkins;

import application.services.CIService;
import domain.model.project.Project;
import infra.jenkins.config.JenkinsServiceConfig;
import infra.jenkins.job.JobRetriever;
import infra.jenkins.job.template.TemplateRetriever;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.JAXBException;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Gregory Boissinot
 */
public class JenkinsService implements CIService {


    private static final Map<String, String> JOB_TYPE = new HashMap<String, String>();
    private final JenkinsServiceConfig jenkinsServiceConfig;
    private final JobRetriever jobRetriever;
    private Logger logger = LoggerFactory.getLogger(JenkinsService.class);

    public JenkinsService(JenkinsServiceConfig jenkinsServiceConfig) {
        this.jenkinsServiceConfig = jenkinsServiceConfig;
        this.jobRetriever = new JobRetriever(new TemplateRetriever(jenkinsServiceConfig.getTemplateDir()));
    }

    @Override
    public void createOrUpdateCIJob(Project project) {
        String jenkinsJobName = getJobName(project);
        //  System.out.println("createOrUpdateCIJob "+ jenkinsServiceConfig.getUsername() +":"+jenkinsServiceConfig.getPassword());
        logger.debug("create or update job {}", jenkinsJobName);
        int rep = 0;
        try {
            rep = createOrUpdateJob(project, !isJenkinsJobExists(jenkinsJobName));
        } catch (IOException e) {
            logger.error("failed to create or update job", e);
            throw new JenkinsException(e);
        } catch (JAXBException e) {
            logger.error("failed to create or  update job", e);
            throw new JenkinsException(e);
        }
        logger.debug("{} result is {}", jenkinsJobName, rep);
    }

    @Override
    public void deleteCIJob(Project project) {
        System.out.println("deleteCIJob");
        String jobName = getJobName(project);
        try {
            if (isJenkinsJobExists(jobName)) {
                HttpURLConnection conn = null;
                try {
                    URL url = new URL(jenkinsServiceConfig.getUrl() + "/job/" + jobName + "/doDelete");
                    conn = (HttpURLConnection) url.openConnection();
                    addAuth(conn, jenkinsServiceConfig.getUsername(), jenkinsServiceConfig.getPassword());
                    conn.setRequestMethod("POST");
                    int responseCode = conn.getResponseCode();
                    BufferedReader br = new BufferedReader(new InputStreamReader(
                            (conn.getInputStream())));

                    String output;
                    logger.debug("server output is:");
                    while ((output = br.readLine()) != null) {
                        logger.debug(output);
                    }
                } catch (IOException e) {
                    logger.error("failed to delete job", e);
                    throw new JenkinsException(e);
                } finally {
                    conn.disconnect();
                }
            }
        } catch (IOException e) {
            logger.error("failed to delete job", e);
            throw new JenkinsException(e);
        } catch (JAXBException e) {
            logger.error("failed to delete job", e);
            throw new JenkinsException(e);
        }

    }


    private int createOrUpdateJob(Project project, boolean createJob)
            throws IOException, JAXBException {
        int responseCode = 00;
        URL url;
        String jobName = getJobName(project);
        if (createJob) {
            url = new URL(jenkinsServiceConfig.getUrl() + "/createItem?name=" + jobName);
        } else {
            url = new URL(jenkinsServiceConfig.getUrl() + "/job/" + jobName + "/config.xml");
        }
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        try {

            addAuth(conn, jenkinsServiceConfig.getUsername(), jenkinsServiceConfig.getPassword());
            conn.setRequestMethod("POST");
            System.out.println(conn);
            OutputStream os = conn.getOutputStream();
            os.write(jobRetriever.getJobConfigXML(project).getBytes());
            os.flush();
            responseCode = conn.getResponseCode();
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    (conn.getInputStream())));

            String output;
            logger.debug("server output is:");
            while ((output = br.readLine()) != null) {
                logger.debug(output);
            }
        } finally {
            conn.disconnect();
        }
        return responseCode;
    }

    private boolean isJenkinsJobExists(String jobName) throws IOException,
            JAXBException {
        URL jobURL = new URL(jenkinsServiceConfig.getUrl() + "/job/" + jobName + "/config.xml");
        HttpURLConnection conn = (HttpURLConnection) jobURL.openConnection();
        boolean isJenkinsJobExists = false;
        try {
            addAuth(conn, jenkinsServiceConfig.getUsername(), jenkinsServiceConfig.getPassword());
            conn.setRequestMethod("GET");
            conn.getInputStream();
            isJenkinsJobExists = true;
        } catch (FileNotFoundException e) {
            // job does not exist
            isJenkinsJobExists = false;
        } finally {
            conn.disconnect();
        }
        return isJenkinsJobExists;
    }


    private String getJobName(Project project) {
        JenkinsRule jenkinsRule = JenkinsRule.getInstance();
        return jenkinsRule.getJobName(project);
    }

    private void addAuth(HttpURLConnection conn, String user, String password) {
        String userPassword = user + ":" + password;
        String encoding = new sun.misc.BASE64Encoder().encode(userPassword.getBytes());
        conn.setRequestProperty("Authorization", "Basic " + encoding);
        conn.setDoOutput(true);
        conn.setRequestProperty("Content-Type", "text/xml");
    }
}
